package com.uuah.server.config.registry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.INamedObject;
import com.uuah.api.lifecycle.IDisposable;
import com.uuah.api.lifecycle.Initialisable;
import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.component.model.IModel;
import com.uuah.server.api.component.service.IService;
import com.uuah.server.api.config.registry.IRegistry;
import com.uuah.server.api.config.registry.IServiceDescriptor;
import com.uuah.server.api.config.registry.IUuahRegistry;
import com.uuah.server.api.endpoint.IEndpointBuilder;
import com.uuah.server.api.endpoint.IEndpointFactory;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.transformer.IBaseTransformer;
import com.uuah.server.api.transport.IConnector;
import com.uuah.server.config.Constants;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.exception.AbstractUuahServerException;
import com.uuah.server.exception.registry.RegistrationException;
import com.uuah.server.exception.registry.ServiceException;
import com.uuah.server.exception.transformer.TransformerException;
import com.uuah.server.util.UuahServerUtils;
import com.uuah.utils.StringUtils;
import com.uuah.utils.UUID;

/**
 * Adds lookup/register/unregister methods for uuah-specific entities to the
 * standard Registry interface.
 *
 * <p>
 * update record:updator，update time，update content and version
 * </p>
 *
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 上午10:03:21
 * @version 1.0.0
 */
public class UuahRegistry implements IUuahRegistry, Initialisable, IDisposable {

	protected Map<String, IBaseTransformer> transformerListCache = new ConcurrentHashMap(
			8);
	protected Map<String, IBaseTransformer> exactTransformerCache = new ConcurrentHashMap(
			8);

	private IRegistry registry;

	protected transient Logger logger = LoggerFactory
			.getLogger(UuahRegistry.class);

	public UuahRegistry(IRegistry registry) {
		this.registry = registry;
	}

	public void initialise() throws InitialisationException {
		// no-op
	}

	public void dispose() {
		exactTransformerCache.clear();
		transformerListCache.clear();
	}

	public IConnector lookupConnector(String name) {
		return (IConnector) registry.lookupObject(name);
	}

	public IUuahEndpoint lookupEndpoint(String name) {
		Object obj = registry.lookupObject(name);
		if (obj instanceof IUuahEndpoint) {
			return (IUuahEndpoint) obj;
		} else {
			if (logger.isDebugEnabled()) {
				logger
						.debug("No endpoint with the name: "
								+ name
								+ "found.  If "
								+ name
								+ " is a global endpoint you should use the EndpointFactory to create endpoint instances from global endpoints.");
			}
			return null;
		}
	}

	public IEndpointBuilder lookupEndpointBuilder(String name) {
		Object o = registry.lookupObject(name);
		if (o instanceof IEndpointBuilder) {
			if (logger.isDebugEnabled()) {
				logger.debug("Global endpoint EndpointBuilder for name: "
						+ name + " found");
			}
			return (IEndpointBuilder) o;
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("No endpoint builder with the name: " + name
						+ " found.");
			}
			return null;
		}
	}

	public IEndpointFactory lookupEndpointFactory() {
		return (IEndpointFactory) registry
				.lookupObject(Constants.OBJECT_UUAH_ENDPOINT_FACTORY);
	}

	public IBaseTransformer lookupTransformer(String name) {
		return (IBaseTransformer) registry.lookupObject(name);
	}

	public IBaseTransformer lookupTransformer(Class inputType, Class outputType)
			throws TransformerException {
		IBaseTransformer result = (IBaseTransformer) exactTransformerCache
				.get(inputType.getName() + outputType.getName());
		if (result != null) {
			return result;
		}
		result = lookupTransformerFromOriCache(inputType, outputType);

		if (result != null) {
			exactTransformerCache.put(inputType.getName()
					+ outputType.getName(), result);
		}
		return result;
	}

	public IBaseTransformer lookupTransformerFromOriCache(Class input,
			Class output) {
		IBaseTransformer result = transformerListCache.get(input.getName()
				+ output.getName());
		if (result != null) {
			return result;
		}

		Collection transformers = getTransformers();
		for (Iterator itr = transformers.iterator(); itr.hasNext();) {
			IBaseTransformer t = (IBaseTransformer) itr.next();
			Class<Object> c = t.getReturnClass();
			if (c == null) {
				c = Object.class;
			}
			if (output.isAssignableFrom(c) && t.isSourceTypeSupported(input)) {
				transformerListCache.put(input.getName() + output.getName(), t);
				return t;
			}
		}

		return result;
	}

	public IModel lookupModel(String name) {
		return (IModel) registry.lookupObject(name);
	}

	public IModel lookupSystemModel() {
		return lookupModel(Constants.OBJECT_SYSTEM_MODEL);
	}

	public Collection getModels() {
		return registry.lookupObjects(IModel.class);
	}

	public Collection getConnectors() {
		return registry.lookupObjects(IConnector.class);
	}

	public Collection getEndpoints() {
		return registry.lookupObjects(IUuahEndpoint.class);
	}

	public Collection getTransformers() {
		return registry.lookupObjects(IBaseTransformer.class);
	}

	public IService lookupService(String name) {
		return (IService) registry.lookupObject(name);
	}

	public Collection<IService> lookupServices() {
		return lookupObjects(IService.class);
	}

	public Collection<IService> lookupServices(String model) {
		Collection<IService> services = lookupServices();
		List modelServices = new ArrayList();
		Iterator it = services.iterator();
		IService service;
		while (it.hasNext()) {
			service = (IService) it.next();
			if (model.equals(service.getModel().getName())) {
				modelServices.add(service);
			}
		}
		return modelServices;
	}

	public final void registerTransformer(IBaseTransformer transformer)
			throws AbstractUuahServerException {
		registry.registerObject(getName(transformer), transformer,
				IBaseTransformer.class);
	}

	public IServiceDescriptor lookupServiceDescriptor(String type, String name)
			throws ServiceException {
		String key = new AbstractServiceDescriptor.Key(name).getKey();
		IServiceDescriptor sd = (IServiceDescriptor) registry.lookupObject(key);

		synchronized (this) {
			if (sd == null) {
				sd = createServiceDescriptor(type, name);
				try {
					registry.registerObject(key, sd, IServiceDescriptor.class);
				} catch (RegistrationException e) {
					throw new ServiceException(e.getI18nMessage(), e);
				}
			}
		}
		return sd;
	}

	protected IServiceDescriptor createServiceDescriptor(String type,
			String name) throws ServiceException {
		Properties props = UuahServerUtils.findServiceDescriptor(type, name);
		if (props == null) {
			throw new ServiceException(ServerMessages.failedToLoad(type + " "
					+ name));
		}
		return ServiceDescriptorFactory.create(type, name, props, registry,
				null);
	}

	public void registerConnector(IConnector connector)
			throws AbstractUuahServerException {
		registry
				.registerObject(getName(connector), connector, IConnector.class);
	}

	public void registerEndpoint(IUuahEndpoint endpoint)
			throws AbstractUuahServerException {
		registry
				.registerObject(getName(endpoint), endpoint, IUuahEndpoint.class);
	}

	public void registerEndpointBuilder(String name, IEndpointBuilder builder)
			throws AbstractUuahServerException {
		registry.registerObject(name, builder, IEndpointBuilder.class);
	}

	public void registerModel(IModel model) throws AbstractUuahServerException {
		registry.registerObject(getName(model), model, IModel.class);
	}

	public void registerService(IService service)
			throws AbstractUuahServerException {
		registry.registerObject(getName(service), service, IService.class);
	}

	public void unregisterService(String serviceName)
			throws AbstractUuahServerException {
		registry.unregisterObject(serviceName, IService.class);
	}

	public void unregisterConnector(String connectorName)
			throws AbstractUuahServerException {
		registry.unregisterObject(connectorName, IConnector.class);
	}

	public void unregisterEndpoint(String endpointName)
			throws AbstractUuahServerException {
		registry.unregisterObject(endpointName, IUuahEndpoint.class);
	}

	public void unregisterModel(String modelName)
			throws AbstractUuahServerException {
		registry.unregisterObject(modelName, IModel.class);
	}

	public void unregisterTransformer(String transformerName)
			throws AbstractUuahServerException {
		IBaseTransformer transformer = lookupTransformer(transformerName);

		registry.unregisterObject(transformerName, IBaseTransformer.class);
	}

	public Object lookupObject(Class type) throws RegistrationException {
		return registry.lookupObject(type);
	}

	public Object lookupObject(String key) {
		return registry.lookupObject(key);
	}

	public Collection lookupObjects(Class type) {
		return registry.lookupObjects(type);
	}

	public void registerObject(String key, Object value, Object metadata)
			throws RegistrationException {
		registry.registerObject(key, value, metadata);
	}

	public void registerObject(String key, Object value)
			throws RegistrationException {
		registry.registerObject(key, value);
	}

	public void registerObjects(Map objects) throws RegistrationException {
		registry.registerObjects(objects);
	}

	public void unregisterObject(String key, Object metadata)
			throws RegistrationException {
		registry.unregisterObject(key, metadata);
	}

	public void unregisterObject(String key) throws RegistrationException {
		registry.unregisterObject(key);
	}

	protected String getName(Object obj) {
		String name = null;
		if (obj instanceof INamedObject) {
			name = ((INamedObject) obj).getName();
		}
		if (StringUtils.isBlank(name)) {
			name = obj.getClass().getName() + ":"
					+ UUID.getTimeBasedUUIDString();
		}
		return name;
	}

	public String getRegistryId() {
		return this.toString();
	}

	public boolean isReadOnly() {
		return false;
	}

	public boolean isRemote() {
		return false;
	}
}
